-
Notifications
You must be signed in to change notification settings - Fork 20
Forward Linux capabilities to child process #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Thank you very much for this contribution. Could you elaborate a bit on the use case? Running desktop Linux since the late 90s, I have never encountered Linux Capabilities. But then, I am mostly just running Linux Live ISOs. Under which circumstances would a normal desktop user benefit from this? Can you think of any security risks this PR might introduce? |
Thank you for your reply. I'd suggest you to read the link I had mentioned, but as it is quite a big read (and I had to read it multiple times to fully grasp it myself), here a really short summary: Linux capabilities had originally been introduced to Linux in version 2.2 (so January 1999) and partition the rights that the root user has so that processes can have a subset of them instead of the whole bunch when simply running as root (including setuid). Capabilities include things like listening on ports <= 1024 (CAP_NET_BIND_SERVICES), changing file ownership (CAP_CHOWN), sending SIGKILL to any process (CAP_KILL) or the ability to change the capabilities set on a file (CAP_SETFCAP). The ability to set the capabilities on a file had been introduced with Linux 2.6.24 and allows a system administrator (aka someone whose processes have the CAP_SETFCAP capability; e.g. root) to allow specific programs to have specific rights without granting it all rights that are granted to root. To give you an example where we use this: we have a product called BootCast which we're currently preparing for a Linux release (as an AppImage) and which provides PXE services, thus it needs to be able to listen to TFTP connections which require a port <= 1024 which in turn normally requires root privileges. Running an application - especially something as complex as a server application - as root can be quite a security problem. By using Linux capabilities this risk potential can be reduced, because a system administrator only needs to apply the CAP_NET_BIND_SERVICES capability to the application file and the Linux kernel will deal with the remainder. Regarding security risks: Feel free to ask other Linux users with more experience with capabilities before accepting this PR as I definitely wouldn't consider myself a “pro” here; we simply had a problem (namely running our product which is an AppImage without root privileges if possible) and implemented a solution (this PR), so we'll continue to maintain our fork due to us needing it, but we'd prefer this to be part of upstream obviously. Also feel free to ask further questions, I'll do my best to answer them. |
Thanks @miray-sb for your detailed answer. tl;dr: Linux capabilities were introduced in version 2.2 to partition the rights of the root user, allowing processes to have a subset of these rights instead of all of them. This feature enables system administrators to grant specific programs only the necessary rights without giving them all root privileges, reducing the risk of security breaches if an application is compromised. By using Linux capabilities, applications can be run with limited privileges, such as listening on ports <= 1024, without requiring full root access, which can help mitigate security risks and reduce the potential for disastrous consequences. Indeed I didn't know about this before, but your explanation does make sense to me. I'd be interested to hear how @TheAssassin sees this, as on the basis of your explanation it seems reasonable to me to forward Linux capabilities to the child process, like you propose. |
Pending @TheAssassin TODO list |
It'd be nice to have this code under an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces a new function to adjust Linux capabilities for child processes, ensuring that capabilities are forwarded appropriately when starting an AppImage.
- Added a new function to modify capability sets via SYS_capget/SYS_capset and prctl calls.
- Integrated the capability adjustment call into the main execution flow.
src/runtime/runtime.c
Outdated
struct __user_cap_header_struct hdr; | ||
struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3]; | ||
int res, datacnt; | ||
unsigned long long capeff, capperm, capinh; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variables 'capeff', 'capperm', and 'capinh' are declared but never used. Consider removing them to clean up the code.
unsigned long long capeff, capperm, capinh; |
Copilot uses AI. Check for mistakes.
src/runtime/runtime.c
Outdated
if (res) | ||
{ | ||
fprintf(stderr, "Failed to set ambient capability %d; error: %d\n", capval, errno); | ||
return 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function returns 1 upon failure in the prctl call while other errors return -1. Consider standardizing error return values for consistency.
return 1; | |
return -1; |
Copilot uses AI. Check for mistakes.
src/runtime/runtime.c
Outdated
@@ -1704,6 +1798,9 @@ int main(int argc, char* argv[]) { | |||
setenv("XDG_CONFIG_HOME", portable_config_dir, 1); | |||
} | |||
|
|||
/* Ensure that capabilities for the AppImage are applied to the children */ | |||
appimage_adjust_capabilities(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return value of appimage_adjust_capabilities() is not checked. Consider verifying the result to handle potential errors appropriately.
appimage_adjust_capabilities(); | |
if (appimage_adjust_capabilities() != 0) { | |
fprintf(stderr, "Error: Failed to adjust capabilities for the AppImage.\n"); | |
exit(EXIT_FAILURE); | |
} |
Copilot uses AI. Check for mistakes.
… then apply currently effective Linux capabilities to the Inheritable set and then apply all the capabilities in both the Effective and Inheritable sets to the Ambient set, so that the child process (which can be unaware of capabilities) can have its capabilities set accordingly. Note: a failure to set these capabilities will not be considered as a total failure as the application might still be able to work for the intended use case (otherwise the user simply needs to start it as root).
6ee6b8c
to
1151a72
Compare
I've rebased our changes, applied the feedback provided by Copilot (with the change that a failure of forwarding the capabilities is not considered a failure for running the application as it might work for the user's usecase nevertheless) and also added a check for the availability of SYS_capset and SYS_capget which should hopefully cover e.g. FreeBSD support as well. :) |
Build for testing: |
Build for testing: |
When a user starts an AppImage from inside a shell that has certain Linux capabilities set, but the AppImage itself has none set, then the child process will have those capabilities enabled thus allowing the child process certain privileged functionality (e.g. listening on ports <= 1024).
However if capabilities are set on the AppImage file (using setcap) then the child process won't inherit any capabilities due to how capabilities are handled in that case (the Ambient set will be set to 0 which is what a process unaware of capabilities relies on).
This can be worked around by extracting the AppImage, setting the capabilities on the executable file and then running that. However this isn't a very convenient solution.
Thus we propose the extension contained in this merge request: before doing the
execve
for the child process all currently effective capabilities will be added to the Inheritable set and then all capabilities in both the Effective and Inheritable sets will be added to the Ambient set (only capabilities that are in both the Effective and Inheritable sets can be raised in the Ambient set).This way a binary inside the AppImage can make use of privileged operations without running as root, but the system administrator still has full control what the AppImage can do.